package com.rgh.pecunia.core;

import com.googlecode.objectify.Key;
import java.io.Serializable;

import com.rgh.pecunia.domain.Account;
import com.rgh.pecunia.domain.Transfer;
import java.util.Date;

public class PecuniaBusiness implements Serializable {

    // UID
    private static final long serialVersionUID = 1L;
    
    // Valid key characters (letters & numbers)
    public static char[] KEYCHARS = new char[] { 'A', 'B', 'C', 'D', 'E', 
        'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
        'T', 'U', 'V', 'X', 'Y', 'W', 'Z', '0', '1', '2', '3', '4', '5', '6', 
        '7', '8', '9'}; 
    
    // Data access object
    private DataAccessObject dao;
    
    /**
     * 
     * @param dao 
     */
    public PecuniaBusiness(DataAccessObject dao) {
        this.dao = dao;
    }

    /**
     * Generates an unique key for accounts, always starting with a letter.
     * 
     * @return 
     */
    public String generateAccountKey() {
        
        // Auxiliary vars
        boolean ok = false;
        char[] key = new char[4];
        
        // We loop until we have a unique key
        while (!ok) {
            
            // Generate a key that at least starts with a char
            for (int i = 0; i < 4; i++) {
                if (i == 0) {
                    char k;
                    do {
                        k = KEYCHARS[Double.valueOf(
                                Math.random() * KEYCHARS.length).intValue()];
                    } while (!Character.isLetter(k));
                    key[i] = k;
                } else {
                    key[i] = KEYCHARS[Double.valueOf(
                            Math.random() * KEYCHARS.length).intValue()];
                }
            }
            
            // Only OK if no account already uses the key
            ok = dao.query(Account.class).
                    filter("key", new String(key)).count() == 0;
        }
        
        // Return the key
        return new String(key);
    }
    
    /**
     * Transfers an amount from one account to another.
     * 
     * @param source Account where the money comes from
     * @param dest Account where the money goes to
     * @param amount Amount of money transfered
     * @param description Description of the transaction
     * @return true in case of success, false otherwise
     */
    public boolean transfer(String source, String dest, Double amount, 
            String description) {
        
        // Status
        boolean ok = false;
        
        // Validate
        if (source == null 
                || dest == null 
                || amount == null 
                || amount <= 0d
                || source.equalsIgnoreCase(dest)) {
            // Not ok at all
            return false;
        }
        
        try {
            
            // Get accounts
            Account sourceObj = dao.query(Account.class).
                    filter("key", source).get();
            Account destObj = dao.query(Account.class).
                    filter("key", dest).get();
            
            // Validate accounts
            if (sourceObj == null || destObj == null) {
                return false;
            }
            
            // Get objectify keys
            Key<Account> sourceKey = dao.query(Account.class).
                    filter("key", source).getKey();
            Key<Account> destKey = dao.query(Account.class).
                    filter("key", dest).getKey();
            
            // Create transfer
            Transfer transfer = new Transfer();
            transfer.setAmount(amount);
            transfer.setDescription(description);
            transfer.setSource(sourceKey);
            transfer.setDestination(destKey);
            
            // Update accounts
            destObj.setUpdated(new Date());
            sourceObj.setUpdated(new Date());
            destObj.setBalance(destObj.getBalance() + transfer.getAmount());
            sourceObj.setBalance(sourceObj.getBalance() - transfer.getAmount());
            
            // Persist everything
            dao.put(transfer);
            dao.put(sourceObj);
            dao.put(destObj);
            
            // All is well!
            ok = true;
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        
        return ok;
    }
    
}
